import md5 from 'js-md5'
import { defineComponent, reactive, readonly, ref } from 'vue'
import { NForm, NFormItem, NUpload, NButton, UploadInst, UploadCustomRequestOptions } from 'naive-ui'

import { uploadFile, type RequestUploadFileChunk } from '@/api/upload'

function chunk(file: File, start: number, end: number) {
  return file.slice(start, end)
}

function createHash(blob: Blob): Promise<string> {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()

    reader.addEventListener('load', event => {
      const value = md5(event.target?.result as ArrayBuffer)
      resolve(value)
    })

    reader.addEventListener('error', event => {
      console.log(event)
      reject(new Error('file 转 ArrayBuffer 失败'))
    })

    reader.readAsArrayBuffer(blob)
  })
}

async function createChunk(file: File) {
  /**
   * 默认每个 chunk 100kb 大小
   */
  const chunkSize = 1024 * 100
  const chunkTotal = Math.ceil(file.size / (100 * 1024))
  const data: RequestUploadFileChunk[] = []

  let chunkOffset = 1

  while (chunkOffset <= chunkTotal) {
    const end = chunkOffset * chunkSize
    const start = (chunkOffset - 1) * chunkSize

    data.push({
      hash: '',
      name: file.name,
      size: file.size,
      index: chunkOffset,
      total: chunkTotal,
      chunk: chunk(file, start, end)
    })

    chunkOffset++
  }

  for (const item of data) {
    item.hash = await createHash(item.chunk)
  }

  return data
}

function uploadQueue(data: RequestUploadFileChunk[]) {
  let next = Promise.resolve()

  const queue = function (index: number) {
    if (index > data.length - 1) return

    next = next.then(() => uploadFile(data[index]).then(() => queue(index + 1)))
  }

  queue(0)
}

export default defineComponent({
  name: 'uploadView',
  setup() {
    const form = reactive<RequestUploadFileChunk[]>([])
    const uploadRef = ref<UploadInst | null>(null)
    // const accept = readonly(['image/png', 'image/jpeg', 'image/bmp'])
    const accept = readonly(['*'])

    async function upload(data: UploadCustomRequestOptions) {
      if (!data.file.file) return

      console.log(data.file.file.size)

      const chunks = await createChunk(data.file.file)
      uploadQueue(chunks)
    }

    return {
      form,
      upload,
      uploadRef,
      accept
    }
  },
  render() {
    return (
      <app-page>
        <NForm labelWidth={120} labelAlign='left'>
          <NFormItem label='请选择文件'>
            <NUpload
              ref='uploadRef'
              listType='text'
              accept={this.accept.join(',')}
              defaultUpload={false}
              custom-request={this.upload}>
              <NButton>上传文件</NButton>
            </NUpload>
          </NFormItem>

          <NFormItem>
            <NButton
              type='primary'
              onClick={() => {
                this.uploadRef?.submit()
              }}>
              提交
            </NButton>
          </NFormItem>
        </NForm>
      </app-page>
    )
  }
})
